#pragma once

#include "../core.h"

namespace sfc::alloc {

struct Layout {
  usize size;
  usize align;

  static auto from_size_align(usize size, usize align) -> Layout {
    return Layout{size, align};
  }

  template <class T>
  static auto for_value(const T&) -> Layout {
    return Layout{sizeof(T), alignof(T)};
  }

  template <class T>
  static auto xnew() -> Layout {
    return Layout{sizeof(T), alignof(T)};
  }

  template <class T>
  static auto array(usize n) -> Layout {
    return Layout{n * sizeof(T), alignof(T)};
  }
};

template <class I>
struct Alloc {
  template <class T>
  auto alloc_value() -> T* {
    return static_cast<T*>(I::alloc(Layout::xnew<T>()));
  }

  template <class T>
  void dealloc_value(T* p) {
    return I::dealloc(p, Layout::xnew<T>());
  }

  template <class T>
  auto alloc_array(usize cnt) -> T* {
    return static_cast<T*>(I::alloc(Layout::array<T>(cnt)));
  }

  template <class T>
  void dealloc_array(T* p, usize cnt) {
    return I::dealloc(p, Layout::array<T>(cnt));
  }
};

struct System : Alloc<System> {
  static auto alloc(Layout layout) -> void*;
  static auto alloc_zeroed(Layout layout) -> void*;
  static void dealloc(void* p, Layout layout);
  static auto realloc(void* p, Layout layout, usize new_size) -> void*;
};

using Global = System;

}  // namespace sfc::alloc
